
#include "Circuito.h"
#include <iostream>
#include <math.h>

using namespace std;

Circuito::Circuito(std::string nom) {
	cant_entradas=0;
	cant_salidas=0;
	this->nombre=nom;
}

Circuito::Circuito(std::list<Entrada*>* lista_entradas,std::list<Salida*>* lista_salidas ,std::list<Compuerta*>* lista_compuertas, std::list<Cable*>* lista_cables,std::list<ElementoExterno*>* lista_elementos_externos ,std::string nom){
	this->L_entradas = *lista_entradas;
	this->L_salidas = *lista_salidas;
	this->L_compuertas = *lista_compuertas;
	this->L_elementos_externos= * lista_elementos_externos;

	this->L_cables = *lista_cables;


	this->cant_entradas = lista_entradas->size();
	this->cant_salidas = lista_salidas->size();
	this->nombre=nom;
}

Circuito::~Circuito() {
	liberar_lista(&L_compuertas);
	liberar_lista(&L_entradas);
	liberar_lista(&L_salidas);
	liberar_lista(&L_elementos_externos);

}




bool Circuito::circuito_listo(){
	bool resultado = true;
	std::list<Compuerta*> lista_compuerta = L_compuertas;
	std::list<Compuerta*>::iterator it_compuerta = lista_compuerta.begin();

	unsigned int i;
	for (i=0; i<lista_compuerta.size(); i++){
		if (!((*it_compuerta)->ready())){
			resultado = false;
		}
		it_compuerta++;
	}
	if(resultado){
		std::list<ElementoExterno*> lista_externo = L_elementos_externos;
		std::list<ElementoExterno*>::iterator it_externo = lista_externo.begin();
		for (i=0; (i<lista_externo.size()); i++){
			if (!((*it_externo)->ready()))
				resultado = false;
			it_externo++;
		}
	}
	if (resultado){
		std::list<Salida*> lista_salida = L_salidas;
		std::list<Salida*>::iterator it_salida = lista_salida.begin();

		for (i=0; i<lista_salida.size(); i++){
			if (!((*it_salida)->ready()))
				resultado = false;
			it_salida++;
		}
	}
	return resultado;
}


std:: list<Salida*> Circuito::get_lista_salidas(){
	return L_salidas;
}

std:: list<Entrada*> Circuito::get_lista_entradas(){
	return L_entradas;
}

std:: list<Compuerta*> Circuito::get_lista_compuertas(){
	return L_compuertas;
}

std:: list<ElementoExterno*> Circuito::get_lista_elementos_externos(){
	return L_elementos_externos;
}

std:: list<Cable*> Circuito::get_lista_cables(){
	return L_cables;
}

void Circuito::agregar_cable(Cable* cable){
	L_cables.push_front(cable);
}

void Circuito::agregar_punto(int x, int y){
	std::list< Cable* >::iterator it;
	it = L_cables.begin();
	(*it)->agregar_punto(x, y);
}

void Circuito::sel_cable_conect_elemexterno(ElementoCircuital* externo){
	ElementoExterno* aux = dynamic_cast<ElementoExterno*>(externo);
	int x = aux->get_posicion_x();
	int y = aux->get_posicion_y();
	int ancho, alto;
	aux->calcular_ancho(&ancho, &alto);
	std::list< Cable* >::iterator iterator;
	iterator = L_cables.begin();
	std::list<PuntoQuiebre*> lista;
	std::list<PuntoQuiebre*>::iterator iterator_punto_i;
	std::list<PuntoQuiebre*>::iterator iterator_punto_f;
	unsigned int auxiliar = L_cables.size();
	unsigned int j;
	for (j = 0; (j< auxiliar); j++){
		lista = (*iterator)->get_lista();
		iterator_punto_i = lista.begin();
		iterator_punto_f = lista.end();
		--iterator_punto_f;
		int x_cable_i = (*iterator_punto_i)->get_x();
		int y_cable_i = (*iterator_punto_i)->get_y();
		int x_cable_f = (*iterator_punto_f)->get_x();
		int y_cable_f = (*iterator_punto_f)->get_y();


		if ((x_cable_f <= x+ancho)&&(x_cable_f>=x)){
			if ((y_cable_f<=y+alto)&&(y_cable_f>=y)){
				(*iterator)->moviendo_fin();
			}
		}

		if ((x_cable_i <= x + ancho)&&(x_cable_i>=x)){
			if ((y_cable_i<=y+alto)&&(y_cable_i>=y)){
				(*iterator)->moviendo_inicio();
			}
		}
		iterator++;
	}
}

void Circuito::selecc_cables_conectados(int x, int y){
	unsigned int j;
	std::list< Cable* >::iterator iterator;
	iterator = L_cables.begin();
	std::list<PuntoQuiebre*> lista;
	std::list<PuntoQuiebre*>::iterator iterator_punto_i;
	std::list<PuntoQuiebre*>::iterator iterator_punto_f;
	unsigned int auxiliar = L_cables.size();
	for (j = 0; (j< auxiliar); j++){
		lista = (*iterator)->get_lista();
		iterator_punto_i = lista.begin();
		iterator_punto_f = lista.end();
		--iterator_punto_f;
		int x_cable_i = ((*iterator_punto_i)->get_x() / TAMANIO)*TAMANIO;
		int y_cable_i = ((*iterator_punto_i)->get_y() / TAMANIO)*TAMANIO;
		int x_cable_f = ((*iterator_punto_f)->get_x() / TAMANIO)*TAMANIO;
		int y_cable_f = ((*iterator_punto_f)->get_y() / TAMANIO)*TAMANIO;
		/*
		 * Si alguna de las puntas del cable coincide con x e y, se selecciona segun corresponda
		 * el tipo de movimiento
		 */
		if ((x_cable_i == x)&&(y_cable_i == y)){
			(*iterator)->moviendo_inicio();
		}
		else if ((x_cable_f == x)&&(y_cable_f == y)){
			(*iterator)->moviendo_fin();
		}
		iterator++;
	}
}

void Circuito::deshacer_seleccion_cables(){
	std::list<Cable*>::iterator it_cable = L_cables.begin();
	unsigned int i;
	for (i=0; i<L_cables.size(); i++){
		(*it_cable)->deshacer_seleccion();
		it_cable++;
	}
}

void Circuito::quitar_cables_elem_ext(ElementoExterno* elemento){
	unsigned int j;
	int ancho;
	int alto;
	elemento->calcular_ancho(&ancho, &alto);
	int x = elemento->get_posicion_x();
	int y = elemento->get_posicion_y();
	std::list< Cable* >::iterator iterator;
	iterator = L_cables.begin();
	std::list<PuntoQuiebre*> lista;
	std::list<PuntoQuiebre*>::iterator iterator_punto_i;
	std::list<PuntoQuiebre*>::iterator iterator_punto_f;
	unsigned int auxiliar = L_cables.size();
	bool encontrado = false;
	for (j = 0; (j< auxiliar); j++){
		lista = (*iterator)->get_lista();
		iterator_punto_i = lista.begin();
		iterator_punto_f = lista.end();
		--iterator_punto_f;
		int x_cable_i = (*iterator_punto_i)->get_x();
		int y_cable_i = (*iterator_punto_i)->get_y();
		int x_cable_f = (*iterator_punto_f)->get_x();
		int y_cable_f = (*iterator_punto_f)->get_y();

		/*
		 * Si el cable coincide con el elemento externo se borra
		 */
		if ((x_cable_f >= x)&&(x_cable_f<=x+ancho)){
			if ((y_cable_f>=y)&&(y_cable_f<=y+alto)){
				delete (*iterator);
				iterator = L_cables.erase(iterator);
				encontrado = true;
			}
		}
		if (!encontrado){
			if ((x_cable_i >= x)&&(x_cable_i<=x+ancho)){
				if ((y_cable_i>=y)&&(y_cable_i<=y+alto)){
					delete (*iterator);
					iterator = L_cables.erase(iterator);
					encontrado = true;
				}
			}
		}
		if (!encontrado){
			iterator++;
		}
		encontrado = false;
	}
}

void Circuito::sacar_cable(Cable* cable){
	std::list<Cable*>::iterator it_cable = L_cables.begin();
	unsigned int auxiliar = L_cables.size();
	bool encontrado = false;
	unsigned int j;
	for (j = 0; ((j< auxiliar)&&(!encontrado)); j++){
		if ((*it_cable)==cable){
			delete (*it_cable);
			L_cables.erase(it_cable);
			encontrado = true;
		}
		it_cable++;
	}
}

void Circuito::quitar_cables(int x, int y){
	unsigned int j;
	std::list< Cable* >::iterator iterator;
	iterator = L_cables.begin();
	std::list<PuntoQuiebre*> lista;
	std::list<PuntoQuiebre*>::iterator iterator_punto_i;
	std::list<PuntoQuiebre*>::iterator iterator_punto_f;
	unsigned int auxiliar = L_cables.size();
	for (j = 0; (j< auxiliar); j++){

		lista = (*iterator)->get_lista();
		iterator_punto_i = lista.begin();
		iterator_punto_f = lista.end();
		--iterator_punto_f;
		int x_cable_i = ((*iterator_punto_i)->get_x() / TAMANIO)*TAMANIO;
		int y_cable_i = ((*iterator_punto_i)->get_y() / TAMANIO)*TAMANIO;
		int x_cable_f = ((*iterator_punto_f)->get_x() / TAMANIO)*TAMANIO;
		int y_cable_f = ((*iterator_punto_f)->get_y() / TAMANIO)*TAMANIO;

		if (((x_cable_i == x)&&(y_cable_i == y))||((x_cable_f == x)&&(y_cable_f == y))){

			delete (*iterator);
			iterator = L_cables.erase(iterator);

		}
		else
			iterator++;
	}
}

void Circuito::recalcular_pos_cables (int posicion_x, int posicion_y){
	unsigned int j;
	std::list< Cable* >::iterator iterator;
	iterator = L_cables.begin();
	unsigned int auxiliar = L_cables.size();
	for (j = 0; (j< auxiliar); j++){
		if ((*iterator)->is_moviendo_extremo()){
			(*iterator)->camino_nuevo_extremo (posicion_x, posicion_y);
		}
		iterator++;
	}
}

void Circuito::recalcular_pos_rel_cables(int pos_x_inicial,int pos_y_inicial, int x, int y){
	unsigned int j;
	std::list< Cable* >::iterator iterator;
	iterator = L_cables.begin();
	unsigned int auxiliar = L_cables.size();
	for (j = 0; (j< auxiliar); j++){
		if ((*iterator)->is_moviendo_extremo()){
			(*iterator)-> recalcular_camino_relativo(pos_x_inicial, pos_y_inicial, x, y);
		}
		iterator++;
	}

}



int Circuito::get_id_maximo(){
	return this->nro_id;
}

bool Circuito::quitar_cable(ElementoCircuital* inicial, ElementoCircuital* final){
	std::list< Cable* >::iterator iterator;
	iterator = L_cables.begin();
	return true;

}



ElementoCircuital* Circuito::nueva_salida(int x, int y, int ID, std::string nom){
	Salida* nueva_salida=new Salida(x, y, ID, nom);
	L_salidas.push_back(nueva_salida);
	cant_salidas++;
	return nueva_salida;
}

ElementoCircuital* Circuito::nueva_or(int x, int y, int ID, int tiempo){
	Compuerta*nueva_or=new Or(x, y, ID, tiempo);
	L_compuertas.push_back(nueva_or);
	return nueva_or;
}

ElementoCircuital* Circuito::nueva_xor(int x, int y, int ID, int tiempo){
	Compuerta* nueva_xor=new Xor(x, y, ID, tiempo);
	L_compuertas.push_back(nueva_xor);
	return nueva_xor;
}

ElementoCircuital* Circuito::nueva_entrada(int x, int y, int ID, std::string nom){
	Entrada* nueva_entrada=new Entrada(x, y, ID, nom);
	L_entradas.push_back(nueva_entrada);
	cant_entradas++;
	return nueva_entrada;
}

ElementoCircuital* Circuito::nueva_not(int x, int y, int ID, int tiempo){
	Compuerta* nueva_not=new Not(x, y, ID, tiempo);
	L_compuertas.push_back(nueva_not);
	return nueva_not;

}

ElementoCircuital* Circuito::nueva_and(int x, int y, int ID, int tiempo){
	Compuerta* nueva_and=new And(x, y, ID, tiempo);
	L_compuertas.push_back(nueva_and);
	return nueva_and;
}

ElementoCircuital* Circuito::nueva_elemento_externo(int x, int y, int ID, std::string IP ,int port, Cliente* cliente,int cant_entrada,int cant_salida, std::string name){
	ElementoExterno* nueva_elemento_externo=new ElementoExterno(x, y, ID, IP, port, cliente, cant_entrada, cant_salida, name);
	L_elementos_externos.push_back(nueva_elemento_externo);
	return nueva_elemento_externo;
}



void Circuito:: borrar_elemento(int ID){
	ElementoCircuital* elemento;
	if ((elemento=buscar_en_lista(&L_compuertas,ID))!=NULL){
		elemento->liberar_conexiones();
		borrar_de_lista(&L_compuertas,ID);
		return;
	}

	if ((elemento=buscar_en_lista(&L_elementos_externos,ID))!=NULL){
		elemento->liberar_conexiones();
		borrar_de_lista(&L_elementos_externos,ID);
		return;
	}

	if ((elemento=buscar_en_lista(&L_salidas,ID))!=NULL){
		borrar_de_lista(&L_salidas,ID);
		cant_salidas--;
		return;
	}

	if ((elemento=buscar_en_lista(&L_entradas,ID))!=NULL){
		elemento->liberar_conexiones();
		borrar_de_lista(&L_entradas,ID);
		cant_entradas--;
		return;
	}

}


std::string Circuito::simular(){
	tabla.limpiar_tabla();
	tabla.set_nro_entradas(cant_entradas);
	tabla.set_nro_salidas(cant_salidas);
	tabla.set_tiempo_transicion(calcular_t_transicion());
	bool resultado;
	int t_transicion;
	//for todas las combinaciones de entradas posibles
	//seteo valores de las entradas
	for(int i=0;i<(pow(2,cant_entradas));i++){
		setear_entradas(i);
		list<Salida*>::const_iterator lit(L_salidas.begin());
		list<Salida*>::const_iterator lend(L_salidas.end());
		if(L_salidas.empty())
			return "-1";
		while(lit!=lend){
			resultado=(*lit)->calcular_salida(1,t_transicion);
			tabla.agregar_salida(resultado,t_transicion);
			++lit;
		}
	}
	return tabla.print_tabla();
}

std::string Circuito:: simular_una_vez(){
	tabla.limpiar_tabla();
	tabla.set_nro_entradas(cant_entradas);
	tabla.set_nro_salidas(cant_salidas);
	tabla.set_tiempo_transicion(calcular_t_transicion());

	int t_transicion;
	bool resultado;
	list<Salida*>::const_iterator lit(L_salidas.begin());
	list<Salida*>::const_iterator lend(L_salidas.end());
	if(L_salidas.empty())
		return "-1";
	while(lit!=lend){
		resultado=(*lit)->calcular_salida(1,t_transicion);
		tabla.agregar_salida(resultado,t_transicion);
		++lit;
	}
	return tabla.print_tabla();
}


int Circuito:: calcular_t_transicion(){
	std::list<Compuerta*>::const_iterator lit(L_compuertas.begin());
	std::list<Compuerta*>::const_iterator lend(L_compuertas.end());
	int tiempo=0;
	if(L_compuertas.empty())
		return 0;
	else{
		while(lit!=lend){
			if((*lit)->ready())
				tiempo+=(*(lit))->get_tiempo();
			lit++;
		}
	}
	return tiempo;
}



void Circuito::setear_entradas(int iteracion){
	std::list<Entrada*>::const_iterator lit(L_entradas.begin());
	std::list<Entrada*>::const_iterator lend(L_entradas.end());
	string aux;
	int dig;

	while((iteracion)>=2){
		dig=iteracion%2;
		aux+=(char)(dig+48);
		iteracion=iteracion/2;
	}
	aux+=(char)(iteracion+48);

	int i=0;
	while((lit!=lend)&&((unsigned)i<aux.length())){
		if(aux[i]=='0')
			(*lit)->set_estado(false);
		else
			(*lit)->set_estado(true);
		lit++;
		i++;
	}

	while(lit!=lend){
		(*lit)->set_estado(false);
		lit++;
	}
	return;
}
